﻿from math import sqrt

class HomVec3(object):
    """Stellt Methoden zum Rechnen mit Homogenen Vektoren und Homogenen Punkten im 3D bereit"""

    def __init__(self, x=0, y=0, z=0, w=0):
        """Je Achse ein Wert und zusaetzlich ein Homogenes Element"""
        self._x = x*1.0/w if w!=0 else x
        self._y = y*1.0/w if w!=0 else y
        self._z = z*1.0/w if w!=0 else z
        self._w = 1 if w!=0 else 0
        # sichergestellt dass w nur 1 oder 0
        
    def normalize(self):
        """Normalisiert den Vektor auf die Laenge 1"""
        if self.is_point():
            raise ArithmeticError, 'Nur Vektor erlaubt'
        x = self.x / self.length()
        y = self.y / self.length()
        z = self.z / self.length()
        w = self._w
        return HomVec3(x, y, z, w)
                      
    def __add__(self, other):
        """Punkt + Vektor = Punkt, Vektor + Vektor = Vektor"""
        if self.is_point() and other.is_point():
            raise ArithmeticError, 'Punkt und Punkt zu addieren ist nicht definiert'
        x = self.x + other.x
        y = self.y + other.y
        z = self.z + other.z
        w = self._w + other._w
        return HomVec3(x, y, z, w)

    def __sub__(self, other):
        """Punkt - Punkt = Vektor, Vektor - Vektor = Vektor"""
        if self.is_vector() and other.is_point():
            return other.__sub__(self)
        x = self.x - other.x
        y = self.y - other.y
        z = self.z - other.z
        w = self._w - other._w
        return HomVec3(x, y, z, w)

    def scalar_product(self, other):
        """Berechnet das Skalarprodukt zweier Vektoren"""
        
        x = self.x * other.x
        y = self.y * other.y
        z = self.z * other.z
        w = self._w * other._w
        return x+y+z+w

    def scalar_multiplication(self, scalar):
        """Skalierung des Vektors * """
##        FIXME: ist das kein Fehler?
        if self.is_point():
            return self
        
        x = self.x * scalar
        y = self.y * scalar
        z = self.z * scalar
        w = self._w
        return HomVec3(x, y, z, w)

    def vector_product(self, other):
        """Kreuzprodukt % """
        if self.is_point() or other.is_point():
            raise ArithmeticError, 'Nur Vektoren erlaubt'
        x = self.y*other.z - self.z*other.y
        y = self.z*other.x - self.x*other.z
        z = self.x*other.y - self.y*other.x
        w = self._w
        return HomVec3(x, y, z, w)

    def length(self):
        """Laenge des Vektors"""
        if self.is_point():
            raise ArithmeticError, 'Laengenberrechung nur fuer Vektor erlaubt'
        x = self.x**2
        y = self.y**2
        z = self.z**2
        return sqrt(x+y+z)

    def __mul__(self, scalar):
        """Skalar Multiplikation (Skalierung)"""
        return self.scalar_multiplication(scalar)
    
    def __mod__(self, other):
        """Kreuzprodukt / Vektorprodukt"""
        return self.vector_product(other)

    def __repr__(self):
        return "HomVec3 (%s %s %s %s) " % (self.x, self.y, self.z ,self._w)
        
    def is_point(self):
        """True wenn Instanz ein Punkt ist"""
        return self._w == 1

    def is_vector(self):
        """True wenn Instanz ein Vektor ist"""
        return self._w == 0

    def _get_x(self):        
        return self._x
    def _set_x(self, x):
        self._x = x
    x = property(_get_x, _set_x)

    def _get_y(self):
        return self._y
    def _set_y(self, y):
        self._y = y
    y = property(_get_y, _set_y)

    def _get_z(self):
        return self._z
    def _set_z(self, z):
        self._z = z
    z = property(_get_z, _set_z)

class Matrix(object):
    """Stellt alle benoetigten Funktionen zum Rechnen mit Matrizen bereit, benoetigt HomVec3"""


    def __init__(self, c1=HomVec3(), c2=HomVec3(), c3=HomVec3(), c4=HomVec3()):
        self._c1 = c1
        self._c2 = c2
        self._c3 = c3
        self._c4 = c4


    def __add__(self, other):
        c1 = self.c1+other.c1
        c2 = self.c2+other.c2
        c3 = self.c3+other.c3
        c4 = self.c4+other.c4

        return Matrix(c1, c2, c3, c4)


    def __sub__(self, other):
        c1 = self.c1-other.c1
        c2 = self.c2-other.c2
        c3 = self.c3-other.c3
        c4 = self.c4-other.c4

        return Matrix(c1, c2, c3, c4)


    def __mul__(self, scalar):
        """Matrix mal Skalar *"""
        c1 = self.c1*scalar
        c2 = self.c2*scalar
        c3 = self.c3*scalar
        c4 = self.c4*scalar

        return Matrix(c1, c2, c3, c4)

    def __mod__(self, other):
        """Matrix mal Vektor oder Matrix mal Matrix, je nach Parameter %"""
        if isinstance(other, HomVec3):
            return self._mult_vec(other)
        else:
            return self._mult_matrix(other)

    def _mult_vec(self, other):  
        x = other.scalar_product(HomVec3(self.c1.x, self.c2.x, self.c3.x, self.c4.x))      
        y = other.scalar_product(HomVec3(self.c1.y, self.c2.y, self.c3.y, self.c4.y))
        z = other.scalar_product(HomVec3(self.c1.z, self.c2.z, self.c3.z, self.c4.z))
        w = other.scalar_product(HomVec3(self.c1._w, self.c2._w, self.c3._w, self.c4._w))

        return HomVec3(x*self.c4.x, y*self.c4.y, z*self.c4.z, w*self.c4._w)


    def _mult_matrix(self, other):
        c1 = self._mult_vec(other.c1)
        c2 = self._mult_vec(other.c2)
        c3 = self._mult_vec(other.c3)
        c4 = self._mult_vec(other.c4)

        return Matrix(c1,c2,c3,c4)

        
## TODO: repr fertig schreiben
##  Matrix multiplikation, und mit einem Vektor

        
    def __repr__(self):
        output = "%s\t%s\t%s\t%s\t\n" %(self.c1.x, self.c2.x, self.c3.x, self.c4.x)
        output += "%s\t%s\t%s\t%s\t\n" %(self.c1.y, self.c2.y, self.c3.y, self.c4.y)
        output += "%s\t%s\t%s\t%s\t\n" %(self.c1.z, self.c2.z, self.c3.z, self.c4.z)
        output += "%s\t%s\t%s\t%s\t\n" %(self.c1._w, self.c2._w, self.c3._w, self.c4._w)

        return output

    def _get_c1(self):        
        return self._c1
    def _set_c1(self, c1):
        self._c1 = c1
    c1 = property(_get_c1, _set_c1)

    def _get_c2(self):        
        return self._c2
    def _set_c2(self, c2):
        self._c2 = c2
    c2 = property(_get_c2, _set_c2)

    def _get_c3(self):        
        return self._c3
    def _set_c3(self, c3):
        self._c3 = c3
    c3 = property(_get_c3, _set_c3)

    def _get_c4(self):        
        return self._c4
    def _set_c4(self, c4):
        self._c4 = c4
    c4 = property(_get_c4, _set_c4)
    
if __name__ == "__main__":
    v1 = HomVec3(1,1,1,0)
    v2 = HomVec3(3,2,3,0)
    v3 = HomVec3(4,6,1,0)
    v4 = HomVec3(6,1,9,0)

    m1 = Matrix(v1,v2,v3,v4)
    m2 = Matrix(v3,v1,v4,v2)

    # Test von Herrn Schwanecke (eigentlich waren es Punkte)
    p1, p2, p3 = HomVec3(2,2,0,0), HomVec3(4,5,0,0), HomVec3(6,3,0,0)
    print "Flächeninhalt:"
    print "1. Variante: ", ((p2-p1)%(p3-p1)).length()/2
    print "2. Variante: ", (p1%p2+p2%p3+p3%p1).length()/2




    
